//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#pragma once

#include "../CSource.h"
#include "GeneratorUtils.h"
#include "GeneratorNativeUtils.h"

namespace Noise
{

    const real DEFAULT_PERLIN_FREQUENCY = 1.0;
    const real DEFAULT_PERLIN_LACUNARITY = 2.0;
    const int DEFAULT_PERLIN_OCTAVE_COUNT = 6;
    const real DEFAULT_PERLIN_PERSISTENCE = 0.5;
    const ENoiseQuality DEFAULT_PERLIN_QUALITY = QUALITY_STD;
    const int DEFAULT_PERLIN_SEED = 0;
    const int PERLIN_MAX_OCTAVE = 30;


	
	//
	// Function Source for Perlin Noise generation
	//

	class CSourcePerlin : public CSource
	{
	// types
	protected:
		typedef CSourcePerlin	Self;


	// variables
	protected:
		real			_frequency;		// Frequency of the first octave.
		real			_lacunarity;	// Frequency multiplier between successive octaves.
		ENoiseQuality	_noiseQuality;	// Quality of the Perlin noise.
		int				_octaveCount;	// Total number of octaves that generate the Perlin noise.
		real			_persistence;	// Persistence of the Perlin noise.
		int				_seed;			// Seed value used by the Perlin-noise function.
		
		static const
			ESource		_sourceType = PERLIN;

	// methods
	public:
		CSourcePerlin () :
			_frequency(DEFAULT_PERLIN_FREQUENCY),
			_lacunarity(DEFAULT_PERLIN_LACUNARITY),
			_noiseQuality(DEFAULT_PERLIN_QUALITY),
			_octaveCount(DEFAULT_PERLIN_OCTAVE_COUNT),
			_persistence(DEFAULT_PERLIN_PERSISTENCE),
			_seed(DEFAULT_PERLIN_SEED)
		{}

		~CSourcePerlin ()
		{}

		ESourceState GetState () const
		{
			return SOURCE_COMPLETE;
		}
		
		virtual void Delete ()
		{
		}

		virtual EResult GetInteger (ESourceQuery type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case SOURCE_STATE :
					v[0] = GetState();
					return OK;

				case SOURCE_TYPE :
					v[0] = _sourceType;
					return OK;

				case SOURCES_COUNT :
					v[0] = 1;
					return OK;

				case INPUT_SOURCES :
					v[0] = 0;
					return OK;

				case MAX_OCTAVES :
					v[0] = PERLIN_MAX_OCTAVE;
					return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, const int *v, int count)
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_OCTAVE_COUNT	: _octaveCount = Min( v[0], BILLOW_MAX_OCTAVE );	return OK;
				case ARG_NOISE_QUALITY	: _noiseQuality = (ENoiseQuality)v[0];				return OK;
				case ARG_SEED			: _seed = v[0];										return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, const real *v, int count)
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_FREQUENCY		: _frequency = v[0];	return OK;
				case ARG_LACUNARITY		: _lacunarity = v[0];	return OK;
				case ARG_PERSISTENCE	: _persistence = v[0];	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult GetArg (EArgument type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_OCTAVE_COUNT	: v[0] = _octaveCount;	return OK;
				case ARG_NOISE_QUALITY	: v[0] = _noiseQuality;	return OK;
				case ARG_SEED			: v[0] = _seed;			return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult GetArg (EArgument type, real *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_FREQUENCY		: v[0] = _frequency;	return OK;
				case ARG_LACUNARITY		: v[0] = _lacunarity;	return OK;
				case ARG_PERSISTENCE	: v[0] = _persistence;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual CSourcePtr Clone () const override
		{
			Self *	src = new Self();

			src->_frequency		= _frequency;
			src->_lacunarity	= _lacunarity;
			src->_noiseQuality	= _noiseQuality;
			src->_octaveCount	= _octaveCount;
			src->_persistence	= _persistence;
			src->_seed			= _seed;

			return src;
		}
		
		virtual void PreBuild (const TPreBuildData &data) override
		{
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			Utils::NoiseGenFunctions( data );

			if ( data.AddSourceType( _sourceType ) )
			{
				data.source << TOSTRING(
					real Perlin (real3 vPoint, real _frequency, real _lacunarity, real _persistence,\n
								 int _noiseQuality, int _octaveCount, int _seed)\n
					{\n
						real value = 0.0;\n
						real signal = 0.0;\n
						real persistence = 1.0;\n
						int seed;\n
						real3 n;\n
						real3 p = vPoint * _frequency;\n
						\n
						for (int octave = 0; octave < _octaveCount; octave++)\n
						{\n
							n = MakeInt32Range(p);\n
							seed = (_seed + octave) & 0xffffffff;\n
							signal = GradientCoherentNoise3D( n, seed, _noiseQuality );\n
							value += signal * persistence;\n
							p *= _lacunarity;\n
							persistence *= _persistence;\n
						}\n
						return value;\n
					}\n\n
				);
			}

			CSource::BuildFunctionHeader( data.source, _id )
				<< " ( Perlin( vPoint, "
				<< _frequency << ", " << _lacunarity << ", " << _persistence << ", "
				<< _noiseQuality << ", "  << _octaveCount << ", " << _seed << " ) )\n";

			return true;
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		Real NativeFunction (const TVec<Real,3> &vPoint)
		{
			using namespace NativeUtils;

			typedef TVec<Real,3>	Real3;
			
			Real value = 0.0;
			Real signal = 0.0;
			Real persistence = 1.0;
			int seed;
			Real3 n;
			Real3 p = vPoint * _frequency;

			for (int octave = 0; octave < _octaveCount; octave++)
			{
				n = MakeInt32Range(p);
				seed = (_seed + octave) & 0xffffffff;
				signal = GradientCoherentNoise3D( n, seed, _noiseQuality );
				value += signal * persistence;
				p *= _lacunarity;
				persistence *= _persistence;
			}

			return value;
		}

		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			return NativeFunction< float32, &CSource::NativeFunction32 >( vPoint );
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			return NativeFunction< float64, &CSource::NativeFunction64 >( vPoint );
		})

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new Self();
			return true;
		}
	};

}	// Noise